<template>
    <div class="container">
        <div ref="canvasContainer" class="canvas-container"></div>
       <!-- 左侧工具栏 -->
       <LeftToolbar  :type="1" :selectedNode="selectedNode"  :nodeList="nodeList" v-model:name="name" v-model:show="show" :leftToolbarList="leftToolbarList" @back="router.go(-1)"  />

        <!-- 属性配置 -->
        <div class="right" v-if="selectedNode && show" :class=" show? 'slide-in-right' : 'slide-out-right' ">
            <div class="right-tabs">
                <div v-if="bottomToolbarActive != 0 && bottomToolbarActive != 1 && bottomToolbarActive != 5" :class="rightTabs === 0?'right-tabs-active':'right-tabs-item' " @click="rightTabs = 0">配置
                </div>
                <div v-if="bottomToolbarActive!=4" :class="rightTabs === 1?'right-tabs-active':'right-tabs-item' " @click="rightTabs = 1">基本信息</div>
            </div>
            <div class="divider"></div>
            <div class="right-info" v-if="rightTabs === 1 && bottomToolbarActive !=4 &&selectedNode.label ">
                <div class="right-info-name">{{ selectedNode.label }}</div>
                <div class="right-info-item">
                    <div class="right-info-item-label">厂家</div>
                    <div class="right-info-item-value">{{ selectedNode.manufactor }}</div>
                </div>
                <div class="right-info-item">
                    <div class="right-info-item-label">型号</div>
                    <div class="right-info-item-value">{{ selectedNode.model }}</div>
                </div>
                <div class="right-info-item">
                    <div class="right-info-item-label">作用</div>
                    <div class="right-info-item-value">{{ selectedNode.remark }}</div>
                </div>

            </div>
            <div class="right-config" v-else>
                <div class="right-config-line" v-if="selectedNode.line && bottomToolbarActive !=5">
                    <div class="right-config-line-error" v-if="selectedNode.errorMsg">{{ selectedNode.errorMsg }}</div>
                    <div class="right-config-line-node">
                        <div class="right-config-line-node-label">前节点</div>
                        <el-select placeholder="请选择前节点" :disabled="true" v-model="lineStartId" >
                            <template v-for="node in nodeList" :key="node.id">
                                <el-option v-if="node.id != lineEndId" :label="node.label" :value="node.id" />
                            </template>
                        </el-select>
                    </div>
                    <div class="right-config-line-node">
                        <div class="right-config-line-node-label">后节点</div>
                        <el-select placeholder="请选择后节点" :disabled="true" v-model="lineEndId">
                            <template v-for="node in nodeList" :key="node.id">
                                <el-option v-if="node.id != lineStartId" :label="node.label" :value="node.id" />
                            </template>
                        </el-select>
                    </div>
                </div>
            </div>
        </div>
        <Topology :class=" show? 'slide-in-right' : 'slide-out-right' " ref="topologyRef" v-if="show && nodeList.length" :nodeList="nodeList" :data="connectionsList" />
    </div>
</template>

<script setup name="Design">
import { ref, onMounted, onBeforeUnmount, watch } from 'vue'
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js'
import { DragControls } from 'three/examples/jsm/controls/DragControls.js'
import { throttle } from 'lodash-es'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader';
import { getEquipmetVos, updateImg } from "@/api/simulation/design.js";
import { getCurrentInstance } from 'vue'
import LeftToolbar from './components/LeftToolbar.vue'
import Topology from './components/Topology.vue'
import { mergeGeometries, mergeVertices } from "three/addons/utils/BufferGeometryUtils.js";
import { saveModel, getModelById, updateMpdel } from '@/api/simulation/design'
const router = useRouter();
const { proxy } = getCurrentInstance()
const topologyRef = ref(null);
// 场景元素
const canvasContainer = ref(null);
let scene, camera, renderer, controls, dragControls, viewHelper;

// 节点数据
const nodeList = ref([])
const connections = ref(new Map())
let connectionsList = [];
const pointList = [];
// 选中状态相关
const selectedNode = ref(null);
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
const models = {};//保存模型实例
const leftToolbarList = ref([]);
const equipmentList = ref([]);//所有设备列表

const name = ref("未命名文件");
const rightTabs = ref(1);
const bottomToolbarActive = ref(0);
const show = ref(true); //两边工具栏是否显示
const lineColor = 0x00e09e;//绿色
const lineErrorColor = 0xff0000;//红色
const lineStartId = ref(null);//当前选中的线段前节点
const lineEndId = ref(null);//后节点
let formData = {};
let id;


//场景数据
const initSceneData = () => {
    //线
    formData.modelContent.lines.forEach(i => {
        // 1. 创建顶点数据
        const positions = new Float32Array([i.array[0], i.array[1], i.array[2], i.array[3], i.array[4], i.array[5]]);
        const geometry = new THREE.BufferGeometry();
        geometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
        // 2. 修正材质参数
        const material = new THREE.LineBasicMaterial({
            color: lineColor,    // 确保高对比度颜色
            linewidth: 6,      // 线宽设置
            depthTest: false    // 禁用深度测试防止遮挡
        });
        const line = new THREE.Line(geometry, material);
        if (i.position) {
            line.position.set(i.position.x, i.position.y, i.position.z);
        } else {
            line.position.set(0, 0, 0);
        }

        line.geometry.attributes.position.needsUpdate = true
        line.geometry.computeBoundingSphere();
        // 3. 添加至场景
        scene.add(line);
        line.name = i.id;
        line.updateMatrixWorld(true);
        const startPoint = addControlPoint(new THREE.Vector3(i.array[0], i.array[1], i.array[2]));
        const endPoint = addControlPoint(new THREE.Vector3(i.array[3], i.array[4], i.array[5]));

        startPoint['userData']['type'] = 'point';
        endPoint['userData']['type'] = 'point';
        let sId = i.sPointId;
        let eId = i.ePointId
        endPoint.name = eId;
        startPoint.name = sId;
        //开始点
        pointList.push({
            id: sId,
            lineId: i.id,
            point: startPoint,
            direction: 1
        });
        //结束点
        pointList.push({
            id: eId,
            lineId: i.id,
            point: endPoint,
            direction: 2

        });
        connectionsList.push({
            id: i.id,
            line: line,
            start: null,
            end: null,
            startId: i.startId,
            endId: i.endId,
            sPoint: startPoint,
            ePoint: endPoint,
            sPointId: sId,
            ePointId: eId,
            errorMsg: i.errorMsg,
            status: i.status,//0 正常 1连接线逻辑错误
        });

    })
    // for (const l of connectionsList) {
    //     updateConnections({ object: l.line });
    // }
    // for (const l of pointList) {
    //     updateConnections({ object: l.point });
    // }
    if (connectionsList.length) {
        updateConnections({ object: connectionsList[0].line });
    }
    //节点
    formData.modelContent.nodes.forEach(i => {
        //加载模型
        let glb = models[i.nodeId].clone();
        //设置模型缩放大小
        if (i.scale) {
            glb.scale.set(...i.scale);
        }

        //设置模型旋转角度
        if (i.rotation) {
            glb.rotation.set(...i.rotation);
        }
        // const bbox = new THREE.Box3().setFromObject(glb);
        // const center = new THREE.Vector3();
        // bbox.getCenter(center)
        // glb.position.sub(center); // 将模型中心点对齐到原点
        glb.position.set(i.position[0], i.position[1], i.position[2]); // 设置到目标位置
        glb.userData['type'] = 'model'
        scene.add(glb)
        // 立即渲染
        const newNode = {
            id: i.id,//唯一标识
            nodeId: i.nodeId, //设备id
            label: i.label,//设备名称
            manufactor: i.manufactor,//厂家
            model: i.model,//型号
            meshRef: glb,//模型实例
            afterNode: i.afterNode,//下节点
            firstNode: i.firstNode,//上节点
            typeId: i.typeId //设备类型id
        }
        nodeList.value.push(newNode)

    })

    // for (const node of nodeList.value) {
    //     updateConnections({ object: node.meshRef });
    // }
    if (nodeList.value.length) {
        updateConnections({ object: nodeList.value[0].meshRef });
    }

    //设置相机位置
    if (formData.modelContent.camera) {
        camera.position.set(formData.modelContent.camera.x, formData.modelContent.camera.y, formData.modelContent.camera.z);
    }
    //两侧工具栏显示隐藏
    show.value = formData.modelContent.pack;
    updateTopology();
    requestAnimationFrame(() => {
        renderer.render(scene, camera);
    });
}


/**
 * 根据类型获取设备列表
 */
const getData = async () => {
    id = router.currentRoute.value.query.id;
    if (id) {
        const detialRes = await getModelById({ id: id });
        formData = detialRes.data;
        name.value = formData.modelName;
        if (formData.modelContent) {
            formData.modelContent = JSON.parse(formData.modelContent);
        }
    }

    const res = await getEquipmetVos();
    //将所有设备遍历出来
    res.data.forEach(i => {
        i.imgPath = import.meta.env.VITE_APP_BASE_API + i.imgPath;
        i.equipmentManufactorVos.forEach(j => {
            j.equipments.forEach(k => {
                k.imgPath = import.meta.env.VITE_APP_BASE_API + k.imgVos[0].url;
                equipmentList.value.push(k);
            })
        })
    })
    leftToolbarList.value = res.data;
}


// 场景中点击事件处理
const handleCanvasClick = (event) => {
    const rect = renderer.domElement.getBoundingClientRect()
    mouse.x = ((event.clientX - rect.left) / rect.width) * 2 - 1
    mouse.y = -((event.clientY - rect.top) / rect.height) * 2 + 1
    // 优先检测带有标记的连接线碰撞体
    const clickableObjects = []
    connections.value.forEach(conn => {
        conn.line.traverse(child => {
            if (child.userData.isConnection) clickableObjects.push(child)
        })
    })
    raycaster.setFromCamera(mouse, camera);
    const intersects = raycaster.intersectObjects([...nodeList.value.map(n => n.meshRef), ...connectionsList.map(n => n.line)])
    // 检测是否点击了连接线
    if (intersects.length > 0) {
        const clickedMesh = intersects[0].object
        //遍历模型
        let find = nodeList.value.find(n => n.meshRef === clickedMesh);
        if (!find) {
            //遍历线
            find = connectionsList.find(n => n.line === clickedMesh);
            lineEndId.value = find.endId ? find.endId : null;
            lineStartId.value = find.startId ? find.startId : null;
      
            resetHighlight();
        } else {
            //点击模型 将工具栏切换为0
            if (bottomToolbarActive.value == 4) {
              
                rightTabs.value = 1;
            }
            highlight(find.meshRef, find.id)
            requestAnimationFrame(() => {
                renderer.render(scene, camera);
            });
        }
        selectedNode.value = find;
    }
}

//
const updateTopology = () => {
    topologyRef?.value?.setData(connectionsList);

}

// 原始材质备份
let originalMaterials = {};

const highlight = (mesh, id) => {
    if (mesh.userData.type == 'model') {
        for (const key in originalMaterials) {
            resetHighlight(key)
        }
        originalMaterials = {};
        // 备份原始材质
        originalMaterials[id] = [...mesh.material];
        // 创建高亮材质数组
        const highlightMaterials = originalMaterials[id].map(mat => {
            return mat.clone();
        });
        // 统一修改颜色
        highlightMaterials.forEach(mat => {
            mat.emissive.setHex(0xff461f);
            mat.emissiveIntensity = 16;
        });
        // 应用新材质
        mesh.material = highlightMaterials;
    }
}

const resetHighlight = (id) => {
    //有id恢复其他的材质
    if (id) {
        const mesh = nodeList.value.find(n => n.id == id);
        // 恢复原始材质
        if (mesh && mesh.meshRef) {
            mesh.meshRef.material = originalMaterials[id];
        }
    } else {
        //恢复所有材质
        for (const key in originalMaterials) {
            const mesh = nodeList.value.find(n => n.id == key);
            if (mesh && mesh.meshRef) {
                mesh.meshRef.material = originalMaterials[key];
            }
        }
        originalMaterials = {};
    }
}

// 初始化场景
const initScene = async () => {
    // 渲染器
    scene = new THREE.Scene()
    camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
    renderer = new THREE.WebGLRenderer({ antialias: true, preserveDrawingBuffer: true })
    renderer.setSize(window.innerWidth, window.innerHeight)
    renderer.setClearColor(0x12345A)
    canvasContainer.value.appendChild(renderer.domElement)
    camera.position.set(10, 4, 10)
    camera.lookAt(0, 0, 0)
    scene.background = new THREE.Color(0x12345A); // 背景色
    scene.fog = new THREE.Fog(0x12345A, 0, 80); // 雾效颜色与背景一致

    // 光源
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.5)
    scene.add(ambientLight)
    const directionalLight = new THREE.DirectionalLight(0xffffff, 0.8)
    directionalLight.position.set(4, 10, 6)
    scene.add(directionalLight)
    // 控制器
    controls = new OrbitControls(camera, renderer.domElement)
    // controls.addEventListener('change', () => {
    //     viewHelper.update();
    // });

    //  controls.enabled = false
    // controls.enableDamping = true
    // controls.dampingFactor = 0.02 // 阻尼系数（默认0.05）
    //  controls.autoRotate = false;
    // controls.enablePan = false;    // 禁止平移
    controls.enableRotate = true; // 禁止旋转
    //  controls.screenSpacePanning = true; // 默认值，沿屏幕平面平移
    controls.enableZoom = true;   // 禁止缩放
    // controls.minDistance = 2;      // 相机最小距离
    // controls.maxDistance = 40;     // 相机最大距离
    controls.minPolarAngle = 1;   // 垂直旋转下限（弧度）
    controls.maxPolarAngle = 1.3; // 垂直旋转上限

    // 地面网格
    const gridHelper = new THREE.GridHelper(100, 30)
    gridHelper.material.transparent = true;  // 启用透明
    gridHelper.material.opacity = 0.5;      // 设置透明度
    // gridHelper.position.y = -4
    //  gridHelper.position.z = -10
    gridHelper.rotation.x = 0
    scene.add(gridHelper)


    // 不可见地面
    // const groundGeometry = new THREE.PlaneGeometry(200,200)
    // const groundMaterial = new THREE.MeshBasicMaterial({ visible: false })
    // const ground = new THREE.Mesh(groundGeometry, groundMaterial)
    // ground.rotation.x = -Math.PI / 2
    // scene.add(ground)

    // 显示世界坐标系
    const axesHelper = new THREE.AxesHelper(100);
    axesHelper.position.set(0, 0, 0); // 根据场景尺寸调整坐标

   // scene.add(axesHelper);

    // 显示线段方向指示器
    const arrowHelper = new THREE.ArrowHelper(
        new THREE.Vector3(1, 0, 0), // 方向
        new THREE.Vector3(0, 0, 0), // 起点
        5, 0xff0000
    );
 //   scene.add(arrowHelper);
    await getData();
    await initModels()
    if (id) {
        initSceneData();
    }

    animate()
}

//加载模型
const initModels = async () => {
    try {
        proxy.$modal.loading('资源加载中')
        //加载模型
        const loader = new GLTFLoader();
        const modelUrls = equipmentList.value.map(n => `${import.meta.env.VITE_APP_BASE_URL}${n.files?.[0].url}`);
        const loadPromises = modelUrls.map(url => loader.loadAsync(url));
        await Promise.all(loadPromises).then(res => {
            res.forEach((gltf, index) => {
                gltf.scene.traverse(function (obj) {
                    if (obj.isMesh) {
                        obj.material.emissive = obj.material.color;
                        obj.material.emissiveMap = obj.material.map;
                    }
                });
                // ===== 1. 预处理几何体 =====
                function preprocessGeometry(geometry, matrixWorld) {
                    const clonedGeo = geometry.clone();
                    // 应用世界变换矩阵（解决合并后位置错乱）
                    clonedGeo.applyMatrix4(matrixWorld); // 网页1/
                    // 强制补全必要属性（网页3/网页5）
                    ['uv', 'normal', 'uv1'].forEach(attr => {
                        if (!clonedGeo.attributes[attr]) {
                            const itemSize = attr === 'normal' ? 3 : 2;
                            const array = new Float32Array(clonedGeo.attributes.position.count * itemSize);
                            clonedGeo.setAttribute(attr, new THREE.BufferAttribute(array, itemSize));
                        }
                    });
                    return clonedGeo;
                }
                // ===== 2. 收集与合并几何体 =====
                const geometries = [];
                gltf.scene.traverse(obj => {
                    if (obj.isMesh) {
                        const processedGeo = preprocessGeometry(obj.geometry, obj.matrixWorld);
                        geometries.push(processedGeo);
                    }
                });
                // 合并几何体（支持多材质分组）
                const mergedGeometry = mergeGeometries(geometries, {
                    useGroups: true,   // 网页4
                    keepIndices: false // 优化顶点索引
                });
                // 顶点合并优化（容差0.01米）
                const optimizedGeometry = mergeVertices(mergedGeometry, 0.01); // 网页1/网页2
                // 重新计算法线（网页5）
                optimizedGeometry.computeVertexNormals();
                // ===== 4. 创建合并网格 =====
                const materials = [];
                gltf.scene.traverse(obj => {
                    if (obj.isMesh) materials.push(obj.material);
                });
                const mergedMesh = new THREE.Mesh(optimizedGeometry, materials);
                // ===== 5. 内存清理 =====
                geometries.forEach(geo => geo.dispose()); // 网页2
                models[equipmentList.value[index].id] = mergedMesh;
            });
        });
        proxy.$modal.closeLoading();
    } catch (error) {
        console.log("🚀 ~ initModels ~ error:", error)
        proxy.$modal.msgError('模型加载失败');

    }

}


/**
 * 线的两端
 * @param position 控制点位置
 */
const addControlPoint = (position) => {
    const sphereGeometry = new THREE.SphereGeometry(0.06);
    const sphereMaterial = new THREE.MeshBasicMaterial({ color: lineColor });
    const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
    sphere.position.copy(position);
    scene.add(sphere);
    return sphere;
}


//模型前后节点连接判断 线的颜色设置正常绿色 红色异常 错误提示
const connectionRule = (index) => {
    //线两端的模型
    const sNode = nodeList.value.find(n => n.id == connectionsList[index].startId);
    const eNode = nodeList.value.find(n => n.id == connectionsList[index].endId);
    if (sNode && eNode) {
        //sNode节点的 后节点的设备类型是否包含 eNode节点的设备类型
        let sFind = sNode.afterNode.find(i => i.nodeEquipmentType == eNode.typeId);
        //eNode节点 的前几点设备类型是否包含 sNode节点的设备类型
        let eFind = eNode.firstNode.find(i => i.nodeEquipmentType == sNode.typeId);
        if (sFind && eFind) {
            //遍历所有连接线 限制每个设备能连接的类型数量
            let sNum = 0; //sNode 开始点 配置的后节点连接设备的类型数量
            let eNum = 0; //eNode 结束点 配置的前节点连接设备的类型数量
            connectionsList.forEach(item => {
                //连接线的开始节点等于当前的前节点
                if (item.startId == sNode.id) {
                    //查找对应的后节点
                    let end = nodeList.value.find(n => n.id == item.endId);
                    //后节点的类型等于 等于当前前节点设备类型
                    if (end && end.typeId == sFind.nodeEquipmentType) {
                        sNum++;
                    }
                }
                //连接线的开始节点等于当前的前节点
                if (item.endId == eNode.id) {
                    //查找对应的后节点
                    let start = nodeList.value.find(n => n.id == item.startId);
                    //后节点的类型等于 等于当前前节点设备类型
                    if (start && start.typeId == eFind.nodeEquipmentType) {
                        eNum++;
                    }
                }
            })
            if (sNum <= parseInt(sFind.nodeNum) && eNum <= parseInt(eFind.nodeNum)) {
                //判断前后
                if (sNode.meshRef.position.x < eNode.meshRef.position.x) {
                    connectionsList[index].status = 0;
                    connectionsList[index].line.material.color.set(lineColor);
                } else {
                    connectionsList[index].line.material.color.set(lineErrorColor);
                    connectionsList[index]['errorMsg'] = `${sNode.label}与${eNode.label}连接的设备前后位置错误`;
                    if (connectionsList[index].status == 0) {
                        proxy.$modal.msgError(connectionsList[index]['errorMsg']);
                    }
                    connectionsList[index].status = 1;
                }
            } else {
                //设置线的状态
                connectionsList[index].status = 1;
                connectionsList[index].line.material.color.set(lineErrorColor);
                connectionsList[index]['errorMsg'] = `${sNode.label}与${eNode.label}连接的设备数量超出限制`;
                proxy.$modal.msgError(connectionsList[index]['errorMsg']);
            }
        } else {
            //判断反方向
            let sFind = sNode.firstNode.find(i => i.nodeEquipmentType == eNode.typeId);
            let eFind = eNode.afterNode.find(i => i.nodeEquipmentType == sNode.typeId);
            if (sFind && eFind) {
                //反方向判断   前后位置
                if (sNode.meshRef.position.x > eNode.meshRef.position.x) {
                    connectionsList[index].status = 0;
                    connectionsList[index].line.material.color.set(lineColor);
                } else {
                    connectionsList[index].line.material.color.set(lineErrorColor);
                    connectionsList[index]['errorMsg'] = `${sNode.label}与${eNode.label}连接的设备前后位置错误`;
                    if (connectionsList[index].status == 0) {
                        proxy.$modal.msgError(connectionsList[index]['errorMsg']);
                    }
                    connectionsList[index].status = 1;
                }
            } else {
                connectionsList[index].line.material.color.set(lineErrorColor);
                connectionsList[index]['errorMsg'] = `${sNode.label}与${eNode.label}连接逻辑错误`;
                if (connectionsList[index].status == 0) {
                    proxy.$modal.msgError(connectionsList[index]['errorMsg']);
                }
                connectionsList[index].status = 1;
            }
        }
    }
}




// 更新连线
let updating = false;
const updateConnections = (e) => {
    if (updating || connectionsList.length == 0) return
    updating = true
    //物体
    if (e && e.object && e.object.userData && e.object.userData.type == "model") {
        for (let index = 0; index < connectionsList.length; index++) {
            let s = nodeList.value.find(n => n.id == connectionsList[index].startId);
            let ee = nodeList.value.find(n => n.id == connectionsList[index].endId);
            if (connectionsList[index].startId && connectionsList[index].endId) {
                // 计算目标物体的几何中心（本地坐标系）
                s.meshRef.geometry.computeBoundingBox(); // 必须先计算包围盒
                const center = new THREE.Vector3();
                s.meshRef.geometry.boundingBox.getCenter(center); // 获取几何中心
                ee.meshRef.geometry.computeBoundingBox(); // 必须先计算包围盒
                const eCenter = new THREE.Vector3();
                ee.meshRef.geometry.boundingBox.getCenter(eCenter); // 获取几何中心
                // 将本地中心转换为世界坐标
                const worldCenter = new THREE.Vector3();
                s.meshRef.localToWorld(worldCenter.copy(center));
                const eworldCenter = new THREE.Vector3();
                ee.meshRef.localToWorld(eworldCenter.copy(eCenter));
                // 更新圆点位置（初始同步）
                connectionsList[index].sPoint.position.copy(worldCenter);
                connectionsList[index].ePoint.position.copy(eworldCenter);
                connectionsList[index].sPoint.updateMatrixWorld(true);
                connectionsList[index].ePoint.updateMatrixWorld(true);
                connectionsList[index].line.position.set(0, 0, 0); // 重置物体位置
                connectionsList[index].line.geometry.setFromPoints([connectionsList[index].sPoint.position, connectionsList[index].ePoint.position])
                connectionsList[index].line.geometry.attributes.position.needsUpdate = true
                connectionsList[index].line.geometry.computeBoundingSphere();
                connectionRule(index);
                // break;
            } else if (connectionsList[index].startId) {
                // 计算目标物体的几何中心（本地坐标系）
                s.meshRef.geometry.computeBoundingBox(); // 必须先计算包围盒
                const center = new THREE.Vector3();
                s.meshRef.geometry.boundingBox.getCenter(center); // 获取几何中心
                // 将本地中心转换为世界坐标
                const worldCenter = new THREE.Vector3();
                s.meshRef.localToWorld(worldCenter.copy(center));
                // 更新圆点位置（初始同步）
                connectionsList[index].sPoint.position.copy(worldCenter);
                connectionsList[index].sPoint.updateMatrixWorld(true);
                connectionsList[index].ePoint.updateMatrixWorld(true);
                connectionsList[index].line.position.set(0, 0, 0); // 重置物体位置
                connectionsList[index].line.geometry.setFromPoints([connectionsList[index].sPoint.position, connectionsList[index].ePoint.position])
                connectionsList[index].line.geometry.attributes.position.needsUpdate = true
                connectionsList[index].line.geometry.computeBoundingSphere();
                connectionRule(index);
                break
            } else if (connectionsList[index].endId) {
                ee.meshRef.geometry.computeBoundingBox(); // 必须先计算包围盒
                const center = new THREE.Vector3();
                ee.meshRef.geometry.boundingBox.getCenter(center); // 获取几何中心
                // 将本地中心转换为世界坐标
                const worldCenter = new THREE.Vector3();
                ee.meshRef.localToWorld(worldCenter.copy(center));
                // 更新圆点位置（初始同步）
                connectionsList[index].sPoint.position.copy(worldCenter);
                connectionsList[index].sPoint.updateMatrixWorld(true);
                connectionsList[index].ePoint.updateMatrixWorld(true);
                connectionsList[index].line.position.set(0, 0, 0); // 重置物体位置
                connectionsList[index].line.geometry.setFromPoints([connectionsList[index].sPoint.position, connectionsList[index].ePoint.position])
                connectionsList[index].line.geometry.attributes.position.needsUpdate = true
                connectionsList[index].line.geometry.computeBoundingSphere();
                connectionRule(index);
                break
            } else {

            }
        }
    }
    //线
    if (e && e.object && e.object.type == "Line") {
        connectionsList.forEach(i => {
            i.line.geometry.attributes.position.needsUpdate = true;
            const vertices = i.line.geometry.attributes.position.array;
            //更新线的两端圆点位置
            if (i.sPoint && i.ePoint) {
                i.line.updateMatrixWorld(true);
                // 获取顶点本地坐标
                const localStart = new THREE.Vector3().fromBufferAttribute(
                    i.line.geometry.attributes.position, 0
                );
                const localEnd = new THREE.Vector3().fromBufferAttribute(
                    i.line.geometry.attributes.position, 1
                );
                // 转换为世界坐标
                const worldStart = localStart.applyMatrix4(i.line.matrixWorld);
                const worldEnd = localEnd.applyMatrix4(i.line.matrixWorld);
                // 更新球体位置
                i.sPoint.position.copy(worldStart);
                i.ePoint.position.copy(worldEnd);
            }
        })
    }
    //两端的点
    if (e && e.object && e.object.userData.type == "point") {
        let lineId = pointList.find(p => p.id == e.object.name).lineId;
        let lineObj = connectionsList.find(c => c.id == lineId);
        lineObj.sPoint.updateMatrixWorld(true);
        lineObj.ePoint.updateMatrixWorld(true);
        lineObj.line.position.set(0, 0, 0); // 重置物体位置
        lineObj.line.geometry.setFromPoints([lineObj.sPoint.position, lineObj.ePoint.position])
        lineObj.line.geometry.attributes.position.needsUpdate = true
        lineObj.line.geometry.computeBoundingSphere();
    }
    updating = false

    // 优化渲染触发
    requestAnimationFrame(() => {
        renderer.render(scene, camera);
    });
}



// 窗口大小调整
const onWindowResize = () => {
    camera.aspect = window.innerWidth / window.innerHeight
    camera.updateProjectionMatrix()
    renderer.setSize(window.innerWidth, window.innerHeight)
}

// 生命周期
onMounted(() => {
    initScene()
    // 添加点击事件监听
    renderer.domElement.addEventListener('click', handleCanvasClick)
    window.addEventListener('resize', onWindowResize)
})

onBeforeUnmount(() => {
    window.removeEventListener('resize', onWindowResize)
    // 清理节点
    nodeList.value.forEach(node => scene.remove(node.meshRef))
    connectionsList.forEach(node => scene.remove(node.line))
    renderer.dispose()
    canvasContainer.value?.removeChild(renderer.domElement)
    // 移除点击事件监听
    renderer.domElement.removeEventListener('click', handleCanvasClick)

})

// 动画循环
const animate = () => {
    requestAnimationFrame(animate)
    controls.update();
    renderer.render(scene, camera)


}
</script>

<style scoped lang="scss">
.container {
    position: relative;
    width: 100%;
    height: 100%;
    overflow: hidden;
}

.canvas-container {
    width: 100%;
    height: 100%;
    touch-action: none;
    background: #12345a;
}
.divider {
    width: 100%;
    height: 1px;
    background: #516473;
}

@keyframes slideInRight {
  from {
    transform: translateX(100%);
  }
  to {
    transform: translateX(0);
  }
}

@keyframes slideOutRight {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(100%);
  }
}

.slide-in-right {
  animation: slideInRight 0.5s forwards;
}

.slide-out-right {
  animation: slideOutRight 0.5s forwards;
}


.right {
    position: absolute;
    top: 18px;
    right: 18px;
    z-index: 1;
    // height: 100px;
    width: 300px;
    border-radius: 10px;
    background: rgba(0, 17, 46, 0.3);
    backdrop-filter: blur(8px);
    &-info {
        padding: 10px 16px;
        &-name {
            margin-top: 10px;
            color: #fff;
            font-size: 14px;
        }
        &-item {
            min-height: 36px;
            display: flex;
            border-radius: 4px;
            background: #13325a;
            align-items: center;
            color: #fff;
            font-size: 14px;
            margin: 10px 0;
            padding: 4px 10px;

            &-label {
                width: 60px;
            }
            &-value {
                display: flex;
                flex-wrap: wrap;
                gap:4px;
                width: 190px;
            }
        }
    }
    &-config {
        padding: 20px 16px;
        &-line {
            &-error {
                margin-bottom: 10px;
                font-size: 14px;
                color: #ff4d4f;
            }
            &-node {
                display: flex;
                margin-bottom: 10px;
                &-label {
                    color: #fff;
                    font-style: 14px;
                    width: 80px;
                }
                /* 输入框高度/边框/圆角 */
                :deep(.el-select) {
                    border: 1px solid #4893ee;
                    border-radius: 4px;
                    .el-select__wrapper {
                        box-shadow: none;
                        background: transparent;
                        .el-select__selected-item {
                            color: #fff;
                        }
                    }
                }
            }
        }
        &-scale {
            &-label {
                color: #fff;
                font-size: 14px;
                margin-bottom: 16px;
            }
            &-btn {
                margin-top: 20px;
                height: 30px;
                width: 100%;
            }
        }
        &-rotation {
            &-item {
                display: flex;
                align-items: center;
                height: 34px;
                &-label {
                    margin-right: 10px;
                    color: #fff;
                    font-size: 14px;
                }
            }
            &-btn {
                margin-top: 20px;
                height: 30px;
                width: 100%;
            }
        }
        &-del {
            height: 30px;
            width: 100%;
        }
    }
    &-tabs {
        height: 50px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        width: 180px;
        padding-left: 20px;

        &-item {
            cursor: pointer;
            color: #bcbcbc;
            font-size: 16px;
        }
        &-active {
            cursor: pointer;
            color: #fff;
            font-size: 16px;
        }
    }
}
</style>